#include "XMLReader.h"
#include "ComponentTemplate.h"
#include "TextUtils.h"
#include "StringHash.h"

namespace ECS
{

	namespace impl
	{
		template<class T>
		class MemberValueFactory : public IMemberValueFactory
		{
		public:
			virtual MemberValue* create() override
			{
				return new T;
			}

			virtual void parseAttribute(MemberValue* value, const char* attr) override {}

			virtual void parseElement(MemberValue* value, TiXmlElement* element) override {}
		};

		class IntMemberValueFactory : public IMemberValueFactory
		{
		public:
			virtual MemberValue* create() override
			{
				return new PODMemberValue<int>();
			}

			virtual void parseAttribute(MemberValue* value, const char* attr) override 
			{
				((ECS::PODMemberValue<int>*)value)->value = TextUtils::toInt32(attr);
			}

			virtual void parseElement(MemberValue* value, TiXmlElement* element) override 
			{
			}
		};

		class FloatMemberValueFactory : public IMemberValueFactory
		{
		public:
			virtual MemberValue* create() override
			{
				return new PODMemberValue<float>();
			}

			virtual void parseAttribute(MemberValue* value, const char* attr) override 
			{
				((ECS::PODMemberValue<float>*)value)->value = TextUtils::toFloat(attr);
			}

			virtual void parseElement(MemberValue* value, TiXmlElement* element) override 
			{
			}
		};

		class BoolMemberValueFactory : public IMemberValueFactory
		{
		public:
			virtual MemberValue* create() override
			{
				return new PODMemberValue<bool>();
			}

			virtual void parseAttribute(MemberValue* value, const char* attr) override 
			{
				((ECS::PODMemberValue<bool>*)value)->value = TextUtils::toBool(attr);
			}

			virtual void parseElement(MemberValue* value, TiXmlElement* element) override 
			{
			}
		};
	}

	void XMLReader::readAssemblyDef( TiXmlElement* element, AssemblyTemplate* def )
	{
		TiXmlElement* componentElement = element->FirstChildElement();
		while (componentElement != nullptr)
		{
			std::string key = componentElement->Value();
			IXmlTemplateReader* reader = _readers[key];
			if (reader != nullptr)
			{
				IComponentTemplate* item = reader->readComponent(componentElement);
				def->addComponent(item);
			}
			componentElement = componentElement->NextSiblingElement();
		}
	}

	XMLComponentReader::XMLComponentReader()
	{
		registerType<int, impl::IntMemberValueFactory>();
		registerType<float, impl::FloatMemberValueFactory>();
		registerType<bool, impl::BoolMemberValueFactory>();
	}

	void XMLComponentReader::readConnections( ComponentTemplate* ctemplate, MetaClass* classTable, TiXmlElement* element )
	{
		TiXmlElement* connectionsElement = element->FirstChildElement("Connections");
		if (!connectionsElement)
			return;

		TiXmlElement* connectionElement = connectionsElement->FirstChildElement("Connection");
		while (connectionElement)
		{
			const char* signalAttr = connectionElement->Attribute("signal");
			const char* slotAttr = connectionElement->Attribute("slot");
			const char* sourceAttr = connectionElement->Attribute("source");

			if (signalAttr && slotAttr && sourceAttr)
			{
				ComponentConnection connection;
				connection.component = MetaClass::getMetaClass(sourceAttr)->typeID;
				connection.slot = getDynamicHash(slotAttr);
				connection.signal = getDynamicHash(signalAttr);

				ctemplate->connections.push_back(connection);
			}

			connectionElement = connectionElement->NextSiblingElement("Connection");
		}
	}

	void XMLComponentReader::readVariables( ComponentTemplate* ctemplate, MetaClass* classTable, TiXmlElement* element )
	{
		for (auto it = classTable->variables.begin(); it != classTable->variables.end(); ++it)
		{
			MetaVariable* variable = *it;
			IMemberValueFactory* factory = factories[variable->typeID];
			if (factory == nullptr)
			{
				// unknown
				continue;
			}

			MemberValue* value = factory->create();
			value->variable = variable;
			const char* attribute = element->Attribute(variable->name);
			if (attribute)
			{
				factory->parseAttribute(value, attribute);
				ctemplate->values.push_back(value);
			}
			else
			{
				std::string elementName(element->Value());
				elementName += ".";
				elementName += variable->name;

				TiXmlElement* valueElement = element->FirstChildElement(elementName.c_str());

				if (valueElement)
				{
					factory->parseElement(value, valueElement);
					ctemplate->values.push_back(value);
				}
				else
				{
					//set default value
				}
			}
		}
	}

	IComponentTemplate* XMLComponentReader::readComponent(TiXmlElement* element)
	{
		auto tmp = new ComponentTemplate();

		readComponent(tmp, element);
		return tmp;
	}

	void XMLComponentReader::readComponent( ComponentTemplate* ctemplate, TiXmlElement* element )
	{
		const char* className = element->Value();
		ctemplate->classTable = classTable;
		readVariables(ctemplate, classTable, element);
		readConnections(ctemplate, classTable, element);
	}

}
